/******************************************************************************
 * @file bsp_led_handler.c
 *
 * @par dependencies
 *  - bsp_led_driver.h
 *  - bsp_led_handler.h
 *
 * @brief Implements the LED handler module, providing high-level interfaces
 *        for managing and controlling LED hardware operations.
 *
 * Processing flow:
 *
 * call directly.
 *
 * @author iceiceiceberg
 *
 * @date 2025-05-11
 *
 * @version V1.0.0
 *
 * @note 1 tab == 4 spaces
 *
 *****************************************************************************/

//********************************* Includes ********************************//
#include <string.h>

#include "bsp_led_handler.h"

//********************************* Includes ********************************//

//********************************* Defines *********************************//
typedef struct 
{
    uint32_t period;
    uint32_t blink_times;
    blink_proportion_t proportion;
    index_led_instance_t index;
} led_event_t;

/**
 * @brief Analyzes and controls the LED blinking.
 * 
 * Steps:
 *  1. Checks if the driver instance is valid.
 *  2. Copies blinking parameters from the driver instance.
 *  3. Calculates the LED toggle time based on the on/off proportion.
 *  4. Turns the LED according to the calculated toggle time and period.
 *  
 * @param[in] self        : Pointer to the target of driver.
 * 
 * @return led_state_t : Status of the function.
 * 
 * */ 
static handler_state_t led_blink ( bsp_led_driver_t * const self )
{
    handler_state_t ret = HANDLER_OK;

/************** 0.Checking the target status *************/
    if( NULL              == self                   ||
        LED_IS_NOT_INSTED == self->is_instantiated    )
    {
#ifdef DEBUG
        DEBUG_PRINT("HANDLER_ERROR_RESOURCE at led_blink\r\n");
#endif // DEBUG
        ret = HANDLER_ERROR_RESOURCE;
    }

/************** 1.Analyzing the LED blinking *************/
    // 1.1 define the local variables
    uint32_t                                period_ms_local;
    uint32_t                              blink_times_local;
    uint32_t                        proportion_on_off_local;

    // 1.2 copy the local variables from the target
    period_ms_local         =         self->cycle_period_ms;
    blink_times_local       =             self->blink_times;
    proportion_on_off_local = self->blink_proportion_on_off;

    // 1.3 calculate the toggle time of LED
    uint32_t toggle_time_ms = 0;
    if( proportion_on_off_local == PROPORTION_ON_OFF_1_1 )
    {
        toggle_time_ms = period_ms_local / 2;
    }
    else if( proportion_on_off_local == PROPORTION_ON_OFF_1_2 )
    {
        toggle_time_ms = period_ms_local / 3;
    }
    else if( proportion_on_off_local == PROPORTION_ON_OFF_1_3 )
    {
        toggle_time_ms = period_ms_local / 4;
    }
    else
    {
#ifdef DEBUG
        DEBUG_PRINT("LED_ERROR_PARAM in led blinking\r\n");
#endif // DEBUG
        ret = HANDLER_ERROR_PARAM;
        return ret;
    }

/************** 2.Blinking the LED **********************/
    for( uint32_t i = 0; i < blink_times_local; i++ )
    {
        for( uint32_t j = 0; j < period_ms_local; j++)
        {
            if( j < toggle_time_ms )
            {
                self->p_led_ops_instance->pf_led_on();
            }
            else
            {
                self->p_led_ops_instance->pf_led_off();
            }
        }
    }

    return ret;
}

/**
 * @brief Controls the LED operation.
 * 
 * Steps:
 *  1. Checks if the driver instance is valid.
 *  2. Validates the input parameters (period, blink_times, proportion).
 *  3. Stores the input parameters into the driver instance.
 *  4. Calls the led_blink function to execute the blinking.
 *  
 * @param[in] self        : Pointer to the target of driver.
 * @param[in] period      : Period in milliseconds for LED blinking.
 * @param[in] blink_times : Number of times to blink the LED.
 * @param[in] proportion  : On off proportion for LED blinking.
 * 
 * @return led_state_t : Status of the function.
 * 
 * */ 
handler_state_t led_control (
                            bsp_led_handler_t * const                 self, 
                            uint32_t                                period,         
                            uint32_t                           blink_times,         
                            blink_proportion_t                  proportion,
                            index_led_instance_t                      idx )
{
    handler_state_t ret   = HANDLER_OK;
    led_event_t led_event = {0};

/************** 0.Checking the target status *********************************/
    if( NULL              == self                   ||
        LED_IS_NOT_INSTED == self->is_instantiated    )
    {
        ret = HANDLER_ERROR_RESOURCE;
#ifdef DEBUG
        DEBUG_PRINT( "HANDLER_ERROR_RESOURCE at led_control\r\n" );
#endif // DEBUG
        return ret;
    }

/************** 1.Checking the input params **********************************/
    if(
        !(
            ( period < 10000 )                     &&   
            ( blink_times < 100 )                  &&
            ( proportion <= PROPORTION_ON_OFF_1_3 )
         ) // end of !
      )   //end of if
    {
#ifdef DEBUG
    DEBUG_PRINT( "HANDLER_ERROR_PARAM\r\n" );
#endif // DEBUG
        ret = HANDLER_ERROR_PARAM;
        return ret;
    }

/************** 2.Adding the data to the data and event **********************/
    led_event.period              =      period;
    led_event.blink_times         = blink_times;
    led_event.proportion          =  proportion;
    led_event.index               =         idx;
    
    self->led_instance.array_led_driver[idx]->cycle_period_ms  =      period;
    self->led_instance.array_led_driver[idx]->blink_times      = blink_times;
    self->led_instance.array_led_driver[idx]->blink_proportion_on_off\
                                                               =  proportion;

/************** 3.Send the event to the queue ********************************/
    ret = self->p_os_queue->pf_os_queue_put(
                                self->p_queue_handle, 
                                          &led_event, 
                                                  0 );

    if( HANDLER_OK != ret )
    {
#ifdef DEBUG
        DEBUG_PRINT( "Send event error at led_control\r\n" );
#endif // DEBUG
        ret = HANDLER_ERROR_RESOURCE;

        return ret;
    }

#ifdef DEBUG
    DEBUG_PRINT( "LED control event sent successfully\r\n" );
#endif // DEBUG

    return ret;
}

/**
 * @brief Register the LED instance to the handler.
 * 
 * Steps:
 * 1. Check input parameters for NULL pointers.
 * 2. Check if the handler and LED driver are instantiated.
 * 3. Check if the LED handler instance array is full.
 * 4. Register the LED driver instance into the handler's array.
 * 5. Increment the handler's LED instance index.
 * 6. Use critical sections for thread safety if OS_SUPPORT is defined.
 *  
 * @param[in] p_self        : Pointer to the LED handler instance.
 * @param[in] p_led_driver  : Pointer to the LED driver instance to be registered.
 * @param[in] index         : Pointer to the index for the LED instance.
 * @return handler_state_t  : Status of the registration operation.
 * 
 * */
handler_state_t led_register ( bsp_led_handler_t    * const       p_self, 
                               bsp_led_driver_t     * const p_led_driver,
                               index_led_instance_t * const        index ) 
{
    handler_state_t ret = HANDLER_OK;

/************** 1.Checking the input params **********************************/
    if( NULL == p_self || NULL == p_led_driver )
    {
#ifdef DEBUG
        DEBUG_PRINT( "HANDLER_ERROR_PARAM at led_register\r\n" );
#endif // DEBUG

        ret = HANDLER_ERROR_PARAM;
        return ret;
    }
/************** 2.Checking the resource *************************************/
    if( LED_HANDLER_IS_NOT_INSTED == p_self->is_instantiated        ||
        LED_IS_NOT_INSTED         == p_led_driver->is_instantiated   )
    {
#ifdef DEBUG
        DEBUG_PRINT( "HANDLER_ERROR_RESOURCE at led_register\r\n" );
#endif // DEBUG

        ret = HANDLER_ERROR_RESOURCE;
        return ret;
    }

/************** 3.Register LED driver instance *******************************/
    
    // 3.1 Check if the LED handler instance is full
    if( p_self->led_instance.index >= MAX_LED_TARGETS_NUM )
    {
#ifdef DEBUG
        DEBUG_PRINT( "HANDLER_ERROR_RESOURCE at led_register\r\n" );
#endif // DEBUG

        ret = HANDLER_ERROR_RESOURCE;
        return ret;
    }

    // 3.2 Register the LED driver instance
#ifdef OS_SUPPORT
    p_self->p_os_critical->pf_handler_os_critical_enter();
#endif // OS_SUPPORT

    if( p_self->led_instance.index < MAX_LED_TARGETS_NUM )
    {
        p_self->led_instance.array_led_driver[p_self->led_instance.index] \
                                                            = p_led_driver;

        // Set the index to the current position
        *index = p_self->led_instance.index;

        p_self->led_instance.index ++;
    }

#ifdef OS_SUPPORT
    p_self->p_os_critical->pf_handler_os_critical_exit();
#endif // OS_SUPPORT

#ifdef DEBUG
    DEBUG_PRINT( "LED driver instance registered successfully\r\n" );
#endif // DEBUG

    return ret;
}

/**
 * @brief Init the handler_led_instance_t target.
 * 
 * Steps:
 * 1. Initialize the LED index to LED1 (or the starting index).
 * 2. Clear all entries in the LED driver array.
 * 3. Return the initialization status.
 *  
 * @param[in] self        : Pointer to the target of handler_led_instance_t.
 * 
 * */
static handler_state_t led_instance_init( handler_led_instance_t * const self )                    
{
    handler_state_t ret = HANDLER_OK;

    self->index = LED1;
    for( uint32_t i = 0; i < MAX_LED_TARGETS_NUM; i++ )
    {
        self->array_led_driver[i] = ( bsp_led_driver_t * ){0};
    }

    return ret;
}

/**
 * @brief Function implementing LED handler thread.
 * 
 * Steps: TBD: AI to impletment.
 *  
 * @param[in] p_params: Pointer to the LED handler instance.
 * @retval None
 *
 * */
static void led_handler_thread( void * const p_params )
{
    bsp_led_handler_t * self = NULL;
    self = (bsp_led_handler_t *)p_params;
    led_event_t led_event = {0};

    if ( NULL == p_params )
    {
#ifdef DEBUG
        DEBUG_PRINT("LED handler thread received NULL parameters\r\n");
#endif // DEBUG

        return;
    }
    else
    {
        self = (bsp_led_handler_t *)p_params;
    }

#ifdef DEBUG
    DEBUG_PRINT("Starting led_handler_thread task...\n\r");
#endif // DEBUG

    /* Infinite loop */
    for (;;)
    {
        if ( NULL != self)
        {
            // 1. Get the event from the queue
            handler_state_t ret = self->p_os_queue->pf_os_queue_get (
                                                self->p_queue_handle, 
                                                          &led_event, 
                                                                  0 );

            if ( HANDLER_OK == ret )
            {
                led_blink ( self->led_instance.array_led_driver[led_event.index] );
            }

            if ( NULL != self->p_os_delay )
            {
                self->p_os_delay->pf_handler_os_delay( 1000 );
#ifdef DEBUG
                DEBUG_PRINT("led_handler_thread is running\r\n");
#endif // DEBUG
            }
        }

    }
}

/**
 * @brief Instantiates the handler os resource.
 * 
 * Steps:
 * 1. Check input parameters
 * 2. Check if resource is already instantiated
 * 3. Mount external interfaces
 * 4. Initialize LED instance
 * 5. Set instantiation flag
 *  
 * @param[in] self        : Pointer to the target of driver.                     
 * @return handler_state_t : Status of the function.
 * 
 * */
handler_state_t bsp_led_handler_inst_os_resrc_for_quick_boot (
                                              bsp_led_handler_t * const self )
{
    handler_state_t ret = HANDLER_OK;
    
    // 4.1 create the thread
#ifdef OS_SUPPORT
    ret = self->p_os_thread->pf_os_thread_create ( 
                                            led_handler_thread, 
                                          "LED_Handler_Thread", 
                                                           256, 
                                                          self, 
                                                             0, 
                                      &self->p_thread_handle );

    if( HANDLER_OK != ret )
    {
#ifdef DEBUG
        DEBUG_PRINT( "HANDLER_ERROR_MEMORY at os_thread_create\r\n" );
#endif // DEBUG

        return ret;
    }

    // 4.2 create the queue
    ret = self->p_os_queue->pf_os_queue_create(             10, 
                                         sizeof( led_event_t ),
                                        &self->p_queue_handle );

    if( HANDLER_OK != ret )
    {
#ifdef DEBUG
        DEBUG_PRINT( "HANDLER_ERROR_MEMORY at os_queue_create\r\n" );
#endif // DEBUG
        
        return ret;
    }
    
    return ret;

#endif //OS_SUPPORT
}

/**
 * @brief Instantiates the bsp_led_handler_t target.
 * 
 * Steps:
 * 1. Check input parameters
 * 2. Check if resource is already instantiated
 * 3. Mount external interfaces
 * 4. Initialize LED instance
 * 5. Set instantiation flag
 *  
 * @param[in] self        : Pointer to the target of driver.
 * @param[in] sys_time    : Pointer to the core_base_time_ms_t interface.
 * @param[in] os_delay    : Pointer to the os_delay_interface.
 * @param[in] os_queue    : Pointer to the OS queue interface.
 * @param[in] os_critical : Pointer to the OS critical section interface.
 * @return handler_state_t : Status of the function.
 * 
 * */
handler_state_t bsp_led_handler_inst(
                          bsp_led_handler_t           * const             self,
                          handler_core_base_time_ms_t * const         sys_time,
#ifdef OS_SUPPORT
                          handler_os_delay_t          * const         os_delay,
                          handler_os_queue_t          * const         os_queue,
                          handler_os_thread_t         * const        os_thread,
                          handler_os_critical_t       * const      os_critical
#endif // OS_SUPPORT
                          )
{
    handler_state_t ret = HANDLER_OK;
#ifdef DEBUG
    DEBUG_PRINT( "bsp_led_handler_inst() called\r\n" );
#endif // DEBUG

/************** 1.Checking the input params **********************************/
    if( NULL == self            ||
        NULL == sys_time        ||
#ifdef OS_SUPPORT
        NULL == os_delay        ||
        NULL == os_queue        ||
        NULL == os_thread       ||
        NULL == os_critical
#endif // OS_SUPPORT
                                    )
    {
#ifdef DEBUG
    DEBUG_PRINT( "HANDLER_ERROR_PARAM\r\n" );
#endif // DEBUG
        ret = HANDLER_ERROR_PARAM;
        
        return ret;
    }
   
/************** 2.Checking the resource **************************************/
    if( LED_IS_INSTED == self->is_instantiated )
    {
#ifdef DEBUG
    DEBUG_PRINT( "HANDLER_ERROR_RESOURCE\r\n" );
#endif // DEBUG
        ret = HANDLER_ERROR_RESOURCE;
        
        return ret;
    }

/************** 3.Adding the interfaces **************************************/

    // 3.1 mount external interfaces
    self->p_sys_time_base       =     sys_time;

#ifdef OS_SUPPORT
    self->p_os_delay            =     os_delay;  
    self->p_os_queue            =     os_queue; 
    self->p_os_thread           =     os_thread; 
    self->p_os_critical         =     os_critical;
#endif // OS_SUPPORT

    // 3.2 mount internal interfaces
    // API for os layer
    self->pf_led_controler   = led_control;
    // API for internal driver
    self->pf_led_register    = led_register;

/************** 4.Initializing the targets ***********************************/

    // 4.1 create the thread
#ifdef OS_SUPPORT

    bsp_led_handler_inst_os_resrc_for_quick_boot(self);
//    ret = self->p_os_thread->pf_os_thread_create ( 
//                                            led_handler_thread, 
//                                          "LED_Handler_Thread", 
//                                                           256, 
//                                                          self, 
//                                                             0, 
//                                      &self->p_thread_handle );

//    if( HANDLER_OK != ret )
//    {
//#ifdef DEBUG
//        DEBUG_PRINT( "HANDLER_ERROR_MEMORY at os_thread_create\r\n" );
//#endif // DEBUG

//        return ret;
//    }

//    // 4.2 create the queue
//    ret = self->p_os_queue->pf_os_queue_create(             10, 
//                                         sizeof( led_event_t ),
//                                        &self->p_queue_handle );

//    if( HANDLER_OK != ret )
//    {
//#ifdef DEBUG
//        DEBUG_PRINT( "HANDLER_ERROR_MEMORY at os_queue_create\r\n" );
//#endif // DEBUG
//        
//        return ret;
//    }

#endif //OS_SUPPORT

    // 4.3 init LED instance
    ret = led_instance_init( &self->led_instance );
    if( HANDLER_OK != ret )
    {
#ifdef DEBUG
        DEBUG_PRINT( "HANDLER_ERROR_PARAM at led_instance_init()\r\n" );
#endif // DEBUG

    self->p_sys_time_base       =     NULL;

#ifdef OS_SUPPORT
    self->p_os_delay            =     NULL;  
    self->p_os_queue            =     NULL;  
    self->p_os_critical         =     NULL;
#endif // OS_SUPPORT

        return ret;
    }

/************** 6.Adding driver instance flag ********************************/
    self->is_instantiated = LED_HANDLER_IS_INSTED;
#ifdef DEBUG
    DEBUG_PRINT( "LED handler instantiated successfully\r\n" );
#endif // DEBUG

    return ret;
}

//********************************* Defines *********************************//

//********************************* Declaring *******************************//

//********************************* Declaring *******************************//
